package com.dc.task;

import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.dc.common.DateUtil;
import com.dc.constat.DeviceConstants;
import com.dc.entity.Region;
import com.dc.entity.WorkOrder;
import com.dc.enums.DeviceAndWorkOrderType;
import com.dc.enums.HaiWanAlarmDesc;
import com.dc.enums.SystemClass;
import com.dc.enums.WebSocketMessage;
import com.dc.feign.DeviceApiServiceFeign;
import com.dc.feign.SystemApiServiceFeign;
import com.dc.model.Device;
import com.dc.service.DeviceHistoryLogService;
import com.dc.service.DeviceService;
import com.dc.service.RegionService;
import com.dc.service.WorkOrderService;
import com.dc.util.RedisUtil;
import com.dc.util.SnowflakeIdWorker;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StopWatch;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

/**
 * @description: TODO 设备同步
 * @date: 2022/10/28 09:16
 * @author: zhang
 **/
@Slf4j
@Component
@AllArgsConstructor
public class DeviceSynchronousTask {

    private final DeviceService deviceService;
    /**
     * 设备模块
     */
    private final DeviceApiServiceFeign deviceApiServiceFeign;
    /**
     * 系统模块
     */
    private final SystemApiServiceFeign systemApiServiceFeign;
    /**
     * 区域位置
     */
    private final RegionService regionService;
    /**
     * 工单信息
     */
    private final WorkOrderService workOrderService;
    /**
     * redis
     */
    private final RedisUtil redisUtil;
    /**
     * 历史记录穿插
     */
    private final DeviceHistoryLogService logService;

    /**
     * 视频监控 摄像头 数据更新
     */
    @Transactional(rollbackFor = Exception.class)
    @Scheduled(cron = "0 0/6 * * * ?")
    public void videoMonitoring() {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        int page = 1;
        // 调用dc-fire-device 服务
        String startResult = deviceApiServiceFeign.getDeviceInfo(page);
        // 获取到总页码,进行数据操作
        Integer totalPages = JSONUtil.parseObj(startResult).getInt("totalPages");
        Set<Device> resultList = new LinkedHashSet<>();
        // 区域位置
        List<Region> regionList = new LinkedList<>();
        // 记录
        Set<Device> logList = new LinkedHashSet<>();
        // 报警、故障
        Map<String, JSONObject> alarmMap = new HashMap<>();
        Map<String, JSONObject> faultMap = new HashMap<>();

        // 总数据页码
        for (int i = 1; i <= totalPages; i++) {
            String result = deviceApiServiceFeign.getDeviceInfo(i);
            Object list = JSONUtil.parseObj(result).get("list");
            // 拿到数据集合
            if (null != list) {
                JSONArray jsonArray = JSONUtil.parseArray(list.toString());
                for (Object jsonObject : jsonArray) {
                    JSONObject object = JSONUtil.parseObj(jsonObject.toString());
                    String deviceCode = object.getStr("deviceCode");

                    Device device = new Device();
                    // device.setId(object.getLong("deviceId"));
                    device.setSubSystem(SystemClass.VIDEO_MONITORING.getName());

                    // 楼层
                    String floor = object.getStr("deviceCode").split("#")[0].split("-")[1];

                    device.setSerialNumber(deviceCode);
                    device.setName(object.getStr("deviceName"));
                    device.setModel(object.getStr("deviceModel"));
                    device.setDeviceAddress(object.getStr("address"));
                    device.setDeviceFactory(object.getStr("manufacturername"));
                    device.setDeviceType(object.getStr("deviceTypename"));
                    device.setSignalType(object.getInt("videoType"));

                    // 设备状态(诊断问题状态)
                    String deviceStatusName = object.getStr("deviceStatusname");

                    // 所属楼层、楼宇
                    String floorCode = object.getStr("floorCode");
                    String buildingCode = object.getStr("buildingCode");

                    // 区域更新
                    Region regionInfo = getRegion(floor);
                    if (null != regionInfo) {
                        device.setRegionId(regionInfo.getId());
                    } else {
                        // 编号
                        Long aLong = SnowflakeIdWorker.generateId();
                        device.setRegionId(aLong);
                        // 区域位置
                        Region region = new Region();
                        region.setId(aLong);
                        region.setParentId(0L);
                        region.setRegionName(floor);
                        regionList.add(region);
                        regionService.save(region);
                    }
                    device.setRegionName(floor);
                    device.setFloorCode(floor);
                    device.setBuildingCode(buildingCode);

                    // 设备状态 设备状态(诊断问题状态)
                    String deviceStatus = object.getStr("deviceStatus");

                    if ("0".equals(deviceStatus)) {
                        device.setDeviceStatus(0);
                    } else {
                        // 根据情况进行分类
                        // 故障和报警
                        device.setDeviceStatus(1);

                        Date date = new Date();
                        device.setAlarmTime(date);

                        // 状态划分  报警、故障
                        if (!alarmMap.keySet().contains(device.getSerialNumber()) ||
                                !faultMap.keySet().contains(device.getSerialNumber())) {
                            // 报警设备信息
                            JSONObject obj = JSONUtil.parseObj(JSON.toJSONString(device, SerializerFeature.WriteDateUseDateFormat));

                            if (1 == 1) {
                                alarmMap.put(device.getSerialNumber(), obj);
                            } else {
                                faultMap.put(device.getSerialNumber(), obj);
                            }
                            // 工单处理操作
                            workOrderAdd(device, date);

                            // 运行记录
                            logList.add(device);
                        }
                    }

                    // 之前有记录的设备信息
                    Device one = deviceService.getOne(new LambdaQueryWrapper<Device>()
                            .eq(Device::getSerialNumber, deviceCode));
                    if (null != one) {
                        device.setId(one.getId());
                    }
                    resultList.add(device);
                }
            }
        }
        if (resultList.size() > 0) {
            deviceService.saveOrUpdateBatch(distinctDevice(resultList));
        }
        // 推送至前端,推送给所有人
        // 告警信息、故障信息
        if (alarmMap.values().size() > 0) {
            systemApiServiceFeign.sendWebsocketMessage(String.format(WebSocketMessage.ALARM_MESSAGE.getContent(), alarmMap.values().toString()), null);
        }
        if (faultMap.values().size() > 0) {
            systemApiServiceFeign.sendWebsocketMessage(String.format(WebSocketMessage.FAULT_MESSAGE.getContent(), faultMap.values().toString()), null);
        }
        // 记录
        if (logList.size() > 0) {
            logService.insertDevice(distinctDevice(logList));
        }
        stopWatch.stop();
        log.info("({})同步完成,耗时:{}s", SystemClass.VIDEO_MONITORING.getName(), stopWatch.getTotalTimeSeconds());
    }

    /**
     * 根据多个属性 结合去重
     *
     * @param devices
     * @return
     */
    private List<Device> distinctDevice(Set<Device> devices) {
        return devices.stream()
                .collect(Collectors.collectingAndThen(Collectors.toCollection(() ->
                        new TreeSet<>(Comparator.comparing(o -> o.getId() + ";" + o.getSerialNumber()))), ArrayList::new));
    }

    /**
     * 位置信息
     *
     * @param regionName
     * @return
     */
    private Region getRegion(String regionName) {
        return regionService.getOne(new LambdaQueryWrapper<Region>().eq(Region::getRegionName, regionName));
    }

    /**
     * 海湾设备，定时查询
     */
    @Transactional(rollbackFor = Exception.class)
    @Scheduled(cron = "0 0/5 * * * ?")
    public void HaiWanDevice() {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        String result = deviceApiServiceFeign.getHWDeviceInfo();

        // 报警设备信息
        List<JSONObject> alarmList = new LinkedList<>();

        // 转为对应的实体对象
        Device device = JSONUtil.toBean(result, Device.class);

        // 根据设备系列号
        Device serviceOne = deviceService.getOne(new LambdaQueryWrapper<Device>()
                .eq(Device::getSerialNumber, device.getSerialNumber()));

        // 设备信息更新
        if (serviceOne != null) {
            serviceOne.setDeviceStatus(device.getDeviceStatus());
            serviceOne.setDeviceType(device.getDeviceType());
            serviceOne.setUpdateTime(new Date());
            BeanUtils.copyProperties(serviceOne, device);
        } else {
            device.setId(SnowflakeIdWorker.generateId());
            device.setSubSystem(SystemClass.GULF_ACQUISITION.getName());

            device.setName(String.format("%s%s%s", SystemClass.GULF_ACQUISITION.getRemarkName(),
                    DeviceConstants.DEVICE_NAME_FORMAT,
                    device.getSerialNumber()));
            // 数字
            device.setSignalType(1);
            device.setDeviceFactory(SystemClass.GULF_ACQUISITION.getDesc());
        }
        device.setAlarmTime(null);
        // 报警或者故障
        if (DeviceAndWorkOrderType.ALARM_TYPE.getDevCode() == device.getDeviceStatus() ||
                DeviceAndWorkOrderType.FAULT_TYPE.getDevCode() == device.getDeviceStatus()) {
            Date date = new Date();
            device.setAlarmTime(date);
            JSONObject object = JSONUtil.parseObj(JSON.toJSONString(device, SerializerFeature.WriteDateUseDateFormat));
            alarmList.add(object);

            // 工单处理操作
            workOrderAdd(device, date);
        }
        // 设备实时更新
        deviceService.saveOrUpdate(device);
        if (alarmList.size() > 0) {
            // 记录
            device.setCreateTime(new Date());
            logService.insertDevice(Arrays.asList(device));

            String resMessage = WebSocketMessage.getComments(device.getDeviceStatus());
            // 推送至前端,当前在线所有人
            systemApiServiceFeign.sendWebsocketMessage(String.format(resMessage, alarmList.toString()), null);
        }
        stopWatch.stop();
        log.info("({})定时查询,设备信息同步完成,耗时:{}s", SystemClass.GULF_ACQUISITION.getName(), stopWatch.getTotalTimeSeconds());
    }

    /**
     * 处理工单信息
     *
     * @param device
     */
    private void workOrderAdd(Device device, Date date) {
        // 工单添加报警,没有报警信息就记录
        // 报警和故障
        WorkOrder workOrderAlarm = workOrderService.getOne(new LambdaQueryWrapper<WorkOrder>()
                .eq(WorkOrder::getSerialNumber, device.getSerialNumber())
                .eq(WorkOrder::getStatus, 0)
                .eq(WorkOrder::getType, DeviceAndWorkOrderType.getValues(device.getDeviceStatus()))
        );
        if (null == workOrderAlarm) {
            WorkOrder workOrder = new WorkOrder();
            workOrder.setSerialNumber(device.getSerialNumber());
            workOrder.setId(SnowflakeIdWorker.generateId());
            workOrder.setType(DeviceAndWorkOrderType.getValues(device.getDeviceStatus()));
            workOrder.setRepairType(0);

            if (DeviceAndWorkOrderType.ALARM_TYPE.getDevCode() == device.getDeviceStatus()) {
                workOrder.setNumber(redisUtil.serialNumber("HJCZ"));
                String desc = String.format("xxx位置,设备%s于%s,%s检测到%s并报警",
                        device.getSerialNumber(),
                        DateUtil.formatDate(date, DateUtil.DATE_TIME_FORMAT_YYYY_MM_DD_HH_MI_SS),
                        device.getDeviceType(),
                        HaiWanAlarmDesc.getValues(device.getDeviceStatus()));
                workOrder.setDescription(desc);
            }

            if (DeviceAndWorkOrderType.FAULT_TYPE.getDevCode() == device.getDeviceStatus()) {
                workOrder.setNumber(redisUtil.serialNumber("GZCZ"));
                String desc = String.format("设备%s,位于XXX,于%s,系统报告故障",
                        device.getSerialNumber(),
                        DateUtil.formatDate(date, DateUtil.DATE_TIME_FORMAT_YYYY_MM_DD_HH_MI_SS)
                );
                workOrder.setDescription(desc);
            }
            workOrderService.save(workOrder);
        }
    }

}
